<!--流程查看器：提供流程模型设计查看和流转中的流程环节高亮等流程图查看能力-->
<template>
  <div class="my-process-designer">
    <div class="my-process-designer__container">
      <div class="my-process-designer__canvas" ref="bpmnCanvas"></div>
      <div class="my-legend">
        <p v-for="(item, i) in legendList" :key="`legend${i}`">
          <span :style="{ background: item.color }"></span>
          <span>{{ item.text }}</span>
        </p>
      </div>
    </div>
  </div>
</template>

<script setup>
import BpmnViewer from "bpmn-js/lib/Viewer";
import ModelingModule from "bpmn-js/lib/features/modeling";
import MoveCanvasModule from "diagram-js/lib/navigation/movecanvas";
//import DefaultEmptyXML from '@/components/bpmnProcess/designer/plugins/defaultEmpty'
//import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { formatDate } from "@/utils/formatTime";
// import { isEmpty } from "@/utils/is";
// import ReqTool from "@/utils/request-tool";
import { getDictLabel } from "@/utils/dict";
// import {
//   getProcessViewInfo,
//   getBpmnXmlByProcessDefinitionKey,
// } from "@/api/bpm/process.js";

defineOptions({ name: "BpmProcessViewer" });

const { proxy } = getCurrentInstance();
// const req = new ReqTool(proxy);

// 流程相关字典
const DICT_TYPE = {
  BPM_PROCESS_INSTANCE_RESULT: "comment_type",
};

//const dictData = proxy.useDict(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT);
const dictData = {"comment_type":ref([{"label":"审批中","value":"waiting_approval","elTagType":"default","elTagClass":null,"isDefault":"Y"},{"label":"通过","value":"pass","elTagType":"success","elTagClass":null,"isDefault":"N"},{"label":"退回","value":"back","elTagType":"danger","elTagClass":null,"isDefault":"N"},{"label":"拒绝","value":"reject","elTagType":"default","elTagClass":null,"isDefault":"N"},{"label":"取消","value":"cancel","elTagType":"default","elTagClass":null,"isDefault":"N"},{"label":"移交","value":"transfer","elTagType":"info","elTagClass":null,"isDefault":"N"}])};

//debugger
const props = defineProps({
  processInstanceId: {
    // 流程实例id。流程实例id、流程定义key、value三选一，优先级依次下降
    type: String,
    default: "",
  },
  processDefinitionKey: {
    // 流程定义key。流程实例id、流程定义key、value三选一，优先级依次下降
    type: String,
    default: "",
  },
  value: {
    // BPMN XML 字符串。流程实例id、流程定义key、value三选一，优先级依次下降
    type: String,
    default: "",
  },
  prefix: {
    // 使用哪个引擎:activiti、camunda、flowable
    type: String,
    default: "activiti",
  },
  activityData: {
    // 活动的数据。传递时，可高亮流程
    type: Array,
    default: () => [],
  },
  processInstanceData: {
    // 流程实例的数据。传递时，可展示流程发起人等信息
    type: Object,
    default: () => {},
  },
  taskData: {
    // 任务实例的数据。传递时，可展示 UserTask 审核相关的信息
    type: Array,
    default: () => [],
  },
});

provide("configGlobal", props);

const emit = defineEmits(["destroy"]);

let bpmnModeler;

const xml = ref("");
const activityLists = ref([]);
const processInstance = ref(undefined);
const taskList = ref([]);
const bpmnCanvas = ref();
// const element = ref()
const elementOverlayIds = ref(null);
const overlays = ref(null);

const initBpmnModeler = () => {
  if (bpmnModeler) return;
  bpmnModeler = new BpmnViewer({
    container: bpmnCanvas.value,
    height: "100%",
    bpmnRenderer: {},
    additionalModules: [ModelingModule, MoveCanvasModule],
  });
};

/* 创建新的流程图 */
const createNewDiagram = async (xml) => {
  // 将字符串转换成图显示出来
  let xmlString = xml;
  try {
    let { warnings } = await bpmnModeler.importXML(xmlString);
    if (warnings && warnings.length) {
      warnings.forEach((warn) => console.warn(warn));
    }
    // 高亮流程图
    await highlightDiagram();
    const canvas = bpmnModeler.get("canvas");
    canvas.zoom("fit-viewport", "auto");
  } catch (e) {
    console.error(e);
    // console.error(`[Process Designer Warn]: ${e?.message || e}`);
  }
};

function canvasAddMarker(markerCssMap, id, cssName) {
  markerCssMap[id] = cssName;
}

const getActivityHighlightCss = (activity) => {
  return activity.endTime ? "highlight" : "highlight-todo";
};

const getResultCss = (result) => {
  //debugger
  if (result === "waiting_approval") {
    //1 审批中
    return "highlight-todo";
  } else if (result === "pass") {
    //2 已通过
    return "highlight";
  } else if (result === "back") {
    //5 退回
    return "highlight-return";
  } else if (result === "transfer") {
    //6 委派
    return "highlight-todo";
  } else if (result === "reject" || result === "abort") {
    //3 拒绝
    return "highlight-reject";
  } else if (result === "cancel") {
    //4 已取消
    return "highlight-cancel";
  }else{
    console.log(`发现一个未知的操作类型：${result}`)
    //return "highlight-todo";
  }
  return "";

  // if (result === 1) {
  //   // 审批中
  //   return 'highlight-todo'
  // } else if (result === 2) {
  //   // 已通过
  //   return 'highlight'
  // } else if (result === 3) {
  //   // 不通过
  //   return 'highlight-reject'
  // } else if (result === 4) {
  //   // 已取消
  //   return 'highlight-cancel'
  // } else if (result === 5) {
  //   // 退回
  //   return 'highlight-return'
  // } else if (result === 6) {
  //   // 委派
  //   return 'highlight-return'
  // } else if (result === 7 || result === 8 || result === 9) {
  //   // 待后加签任务完成/待前加签任务完成/待前置任务完成
  //   return 'highlight-return'
  // }
  // return ''
};

/**
 * 根据活动节点的完成开始时间，逐个计算线条的css。
 * 第1种计算情况: 按活动的开始时间升序遍历所有已开始或完成的活动，
 * 当前活动已开始，且前一个有连线活动已经结束时，将前一个活动指向这个活动的线标记为已完成。
 * 第2种计算情况: 当一个活动只有一条出线，且当前活动已结束目标活动已开始，则设置连线为已完成。
 * 第3种计算情况: 当一个活动只有一条进线，且当前活动已开始上一个活动已结束，则设置连线为已完成。
 *
 * 已知的问题是上述三种情况涵盖了大部分连线高亮。但并不完全覆盖所有情况，如：
 * 由于假定两个图元之间只有一条线，所以如果有两条线的话只有一条线会高亮，其他线不会高亮。
 * @param markerCssMap
 * @param flowInfo
 */
const calculateAllLinkElementMarkerCss = (markerCssMap, flowInfo) => {
  // 根据活动顺序计算线条高亮css
  const activityArray = flowInfo.activityArray
  const lineOfTowFlowEleMap = flowInfo.lineOfTowFlowEleMap
  if(activityArray == null || activityArray.length < 1){
    return
  }
  //第1种计算情况
  const beforeActs = []
  for(let i=0; i<activityArray.length; i++){
    const act = activityArray[i]
    if(beforeActs.length > 0){
      const markerCssNow = markerCssMap[act.key]
      let line = null
      let beforeAct = null
      // 倒叙找到有连线的上一个节点
      for (let j = beforeActs.length-1; j > -1; j--) {
        beforeAct = beforeActs[j];
        line = lineOfTowFlowEleMap[`${beforeAct.key}->${act.key}`]
        if(line != null) break
      }
      if(line && line.id){
        //如果找到了连线的前面的活动，则设置连线为已完成
        const markerCssBefore = markerCssMap[beforeAct.key]
        if(markerCssNow && markerCssBefore && markerCssBefore != 'highlight-todo'){
          canvasAddMarker(markerCssMap, line.id, getResultCss("pass"));
        }
      }else{
        //没有找到有连线的活动，这比较少见
        console.log(`发现一个没有连线的活动key:${act.key}`, act)
      }
    }
    beforeActs.push(act)
  }
  // debugger
  // 根据图元连接情况计算线条高亮css
  const flowElements = flowInfo.flowElements
  for(let m=0; m<flowElements.length; m++){
    const flowElement = flowElements[m]
    const flowElementIdOrActKey = flowElement.id
    const lineMap = flowInfo.lineOfOneFlowEle[flowElementIdOrActKey]
    const markerCss = markerCssMap[flowElementIdOrActKey]
    if(!lineMap || !markerCss) {
      continue
    }
    //第2种情况：当前节点的输入线小于等于1，且当前节点已开始，则设置输入线已完成
    const incomingLineArr = lineMap.incomingLineArr
    if(incomingLineArr && incomingLineArr.length < 2){
      for(let i=0; i<incomingLineArr.length; i++){
        const incomingLine = incomingLineArr[i]
        canvasAddMarker(markerCssMap, incomingLine.id, getResultCss("pass"));
      }
      // incoming.sourceRef incoming.targetRef  incoming.conditionExpression
    }
    //第3种情况：当前节点的输出线小于等于1，且当前节点已结束(markerCss不等于highlight-todo)，则设置输出线已完成
    const outgoingLineArr = lineMap.outgoingLineArr
    if(outgoingLineArr && outgoingLineArr.length < 2 && markerCss != 'highlight-todo'){
      for(let i=0; i<outgoingLineArr.length; i++){
        const outgoingLine = outgoingLineArr[i]
        canvasAddMarker(markerCssMap, outgoingLine.id, getResultCss("pass"));
      }
    }
  }
}

/**
 * 计算流程图的一个节点的高亮css值
 * @param markerCssMap 记录id到css值的映射
 * @param flowInfo 流程、活动、任务Map信息
 * @param flowElementIdOrActKey 流程元素id（或者是活动key）
 */
const calculateOneElementMarkerCss = (markerCssMap, flowInfo, flowElementIdOrActKey)=>{
  const flowElement = flowInfo.flowElement[flowElementIdOrActKey]
  const activity = flowInfo.activity[flowElementIdOrActKey]
  //debugger
  const flowElementType = flowElement.$type
  // 结束节点根据流程实例信息设置颜色
  if(flowElementType == 'bpmn:EndEvent'){
    if (flowInfo.processInstance && flowInfo.processInstance.result != "waiting_approval") {
      canvasAddMarker(markerCssMap, flowElement.id, getResultCss(flowInfo.processInstance.result))
    }
    return
  }
  if(!activity || activity.key !== flowElement.id) return
  //其他节点根据活动信息设置颜色
  switch(flowElementType){
    case 'bpmn:UserTask':
      const tasks = activity.customTasks
      if (!tasks || tasks.length < 1) {
        return;
      }
      const task = tasks[tasks.length-1]//taskList.value.findLast((m) => m.id === activity.taskId);
      // 高亮任务
      canvasAddMarker(markerCssMap, flowElement.id, getResultCss(task.result))
      break
    case 'bpmn:ServiceTask':
      //服务任务
      if (activity.startTime && !activity.endTime) {
        //进入执行，标识进行色
        canvasAddMarker(markerCssMap, flowElement.id, getResultCss("waiting_approval"));
      }
      if (activity.endTime) {
        // 执行完成，节点标识完成色, 所有outgoing标识完成色。
        canvasAddMarker(markerCssMap, flowElement.id, getResultCss("pass"));
      }
      break
    case 'bpmn:InclusiveGateway'://包容网关
    case 'bpmn:ExclusiveGateway'://互斥网关
    case 'bpmn:ParallelGateway'://并行网关
    case 'bpmn:StartEvent'://开始节点
    case 'bpmn:IntermediateThrowEvent':
      canvasAddMarker(markerCssMap, flowElement.id, getActivityHighlightCss(activity))
      break
    default:
      console.log(`发现一个未处理的流程元素类型:${flowElement.$type}`)
      canvasAddMarker(markerCssMap, flowElement.id, getActivityHighlightCss(activity))
  }
}

/**
 * 由于部分元素本身有输入和输出，但incoming和outgoing没有值。
 * 所以用代码记录下每个活动的输入线和输出线，存放至lineOfOneFlowEle。
 * 同时记录两个活动之间的连接线，存放至lineOfTowFlowEleMap。
 * @param allLineMap 存放：所有的line
 * @param lineOfOneFlowEle 存放：活动下的输入线和输出线，key格式：actitity.key
 * @param lineOfTowFlowEleMap 存放：两个活动间的连接线，key格式：${sourceRef.id}->${targetRef.id}
 * @param el 元素
 */
const addInOrOutLine = (allLineMap, lineOfOneFlowEle, lineOfTowFlowEleMap, el)=>{
  // console.log(el)
  // debugger
  if(!allLineMap.addInOrOutLineLoop){
    allLineMap.addInOrOutLineLoop = {}
  }
  if(allLineMap.addInOrOutLineLoop[el.id]){
    return//已经处理过的图元，不进行二次处理。避免潜在的递归死循环
  }else{
    allLineMap.addInOrOutLineLoop[el.id] = el
  }
  switch(el.$type){
    case 'bpmn:SequenceFlow':
      const line = el
      if(allLineMap[line.id]){
        return;
      }
      allLineMap[line.id] = el
      const sourceRef = line.sourceRef
      if(!lineOfOneFlowEle[sourceRef.id]){
        lineOfOneFlowEle[sourceRef.id] = {}
      }
      if(!lineOfOneFlowEle[sourceRef.id].outgoingLineArr){
        lineOfOneFlowEle[sourceRef.id].outgoingLineArr = []
      }
      lineOfOneFlowEle[sourceRef.id].outgoingLineArr.push(line)
      const targetRef = line.targetRef
      if(!lineOfOneFlowEle[targetRef.id]){
        lineOfOneFlowEle[targetRef.id] = {}
      }
      if(!lineOfOneFlowEle[targetRef.id].incomingLineArr){
        lineOfOneFlowEle[targetRef.id].incomingLineArr = []
      }
      lineOfOneFlowEle[targetRef.id].incomingLineArr.push(line)
      lineOfTowFlowEleMap[`${sourceRef.id}->${targetRef.id}`] = line
      break
    default:
      let lineInArr = el.incoming
      if(!lineInArr || lineInArr.length < 1) return
      for(let i=0; i<lineInArr.length; i++){
        const line = lineInArr[i]
        addInOrOutLine(allLineMap, lineOfOneFlowEle, lineOfTowFlowEleMap, line)
      }
      let lineOutArr = el.outgoing
      if(!lineOutArr || lineOutArr.length < 1) return
      for(let i=0; i<lineOutArr.length; i++){
        const line = lineOutArr[i]
        addInOrOutLine(allLineMap, lineOfOneFlowEle, lineOfTowFlowEleMap, line)
      }
  }
}

/**
 * 组装流程图元、活动、任务、线条的对应和从属关系
 * @param flowElements 流程图元
 * @param processInstance 流程实例
 * @param activityArray 活动
 * @param taskArray 任务
 */
const getFlowElementMap = (flowElements, processInstance, activityArray, taskArray) => {
  const flowInfo = {
    flowElement:{}, activity:{}, activityByTaskId:{}, task:{},
    allLineMap:{},//所有的线
    lineOfOneFlowEle:{}, //一个节点连接了哪些进线和出线
    lineOfTowFlowEleMap:{}, //两个节点中间是哪个线
    flowElements, processInstance, activityArray, taskArray
  }
  // debugger
  for(let i=0; i<activityArray.length; i++){
    const el = activityArray[i]
    if(el.taskId != null){
      flowInfo.activityByTaskId[el.taskId] = el
    }
    flowInfo.activity[el.key] = el
  }
  for(let i=0; i<taskArray.length; i++){
    const el = taskArray[i]
    flowInfo.task[el.id] = el
    const act = flowInfo.activityByTaskId[el.id]
    if(act){
      if(!act.customTasks){
        act.customTasks = []
      }
      act.customTasks.push(el)
    }
  }
  for(let i=0; i<flowElements.length; i++){
    const el = flowElements[i]
    flowInfo.flowElement[el.id] = el
    if(flowInfo.activity[el.id]){
      const act = flowInfo.activity[el.id]
      if(!act.customFlowElements){
        act.customFlowElements = []
      }
      act.customFlowElements.push(el)
    }
  }
  for(let i=0; i<flowElements.length; i++){
    const el = flowElements[i]
    addInOrOutLine(flowInfo.allLineMap, flowInfo.lineOfOneFlowEle, flowInfo.lineOfTowFlowEleMap, el)
  }
  // console.log(flowInfo.lineOfOneFlowEle)
  // console.log(flowInfo.allLineMap)
  return flowInfo
}

/**
 * 计算流程图里所有元素的marker的css值：第2版本
 */
const calculateAllMarkerCssV2 = () => {
  const markerCssMap = {}
  if (!activityLists.value || activityLists.value.length === 0) {
    return markerCssMap;
  }
  // 获取根元素，逐层遍历计算出所有的元素css
  const bpmnDefinitions = bpmnModeler.getDefinitions();
  const rootElements = bpmnDefinitions.rootElements
  for(let i=0; i<rootElements.length; i++){
    const rootElement = rootElements[i]
    let flowElements = rootElement.flowElements
    if (!flowElements) {
      continue
    }
    const rootElementId = rootElement.id
    const flowInfo = getFlowElementMap(flowElements, processInstance, activityLists.value, taskList.value)
    flowInfo.flowElement[rootElementId] = rootElement
    // 先计算活动节点css
    for(let i=0; i<activityLists.value.length; i++){
      const act = activityLists.value[i]
      calculateOneElementMarkerCss(markerCssMap, flowInfo, act.key)
    }
    // 根据活动节点的完成开始时间，逐个计算线条的css
    calculateAllLinkElementMarkerCss(markerCssMap, flowInfo)
  }
  return markerCssMap;
}

/* 使用canvas的addMarker函数，对流程图进行高亮显示 */
const highlightDiagram = async () => {
  // 参考自 https://gitee.com/tony2y/RuoYi-flowable/blob/master/ruoyi-ui/src/components/Process/index.vue#L222 实现
  // 再次基础上，增加不同审批结果的颜色等等
  let canvas = bpmnModeler.get("canvas");
  const markerCssMap = calculateAllMarkerCssV2()
  for (let id in markerCssMap) {
    if (markerCssMap[id] && markerCssMap[id] != "") {
      canvas.addMarker(id, markerCssMap[id]);
    }
  }
}

const initModelListeners = () => {
  const EventBus = bpmnModeler.get("eventBus");
  // 注册需要的监听事件
  EventBus.on("element.hover", function (eventObj) {
    let element = eventObj ? eventObj.element : null;
    elementHover(element);
  });
  EventBus.on("element.out", function (eventObj) {
    let element = eventObj ? eventObj.element : null;
    elementOut(element);
  });
};

// 鼠标悬浮显示的文本html内容
const getHoverHtml = (element, processInstance, activity)=>{
  let html = `<p>图元ID: ${element.value.id}</p>
              <p>图元类型: ${element.value.type}</p>`; // 默认值
  if (element.value.type === "bpmn:StartEvent" && processInstance.value) {
    html = `<p>发起人：${processInstance.value.startUser.nickname}</p>
            ${processInstance.value.startUser.deptName?
            "<p>部门：" +processInstance.value.startUser.deptName +"</p>": ""
            }
            <p>创建时间：${formatDate(processInstance.value.createTime)}`;
  } else if (element.value.type === "bpmn:UserTask") {
    // 找到活动对应的 taskId (最后一个是最新的)
    let task = taskList.value.findLast((m) => m.id === activity.taskId);
    if (!task) {
      return;
    }
    let dataResult = getDictLabel(
      dictData[DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT].value,
      task.result
    );
    html = `<p>审批人：${task.assigneeUser.nickname}</p>
            ${task.assigneeUser.deptName ?
            "<p>部门：" + task.assigneeUser.deptName + "</p>" : ""
            }
            <p>结果：${dataResult}</p>
            <p>创建时间：${formatDate(task.createTime)}</p>`;
    if (task.endTime) {
      html += `<p>结束时间：${formatDate(task.endTime)}</p>`;
    }
    if (task.reason) {
      html += `<p>审批建议：${task.reason}</p>`;
    }
  } else if (element.value.type === "bpmn:ServiceTask") {
    if (activity.startTime > 0) {
      html = `<p>创建时间：${formatDate(activity.startTime)}</p>`;
    }
    if (activity.endTime > 0) {
      html += `<p>结束时间：${formatDate(activity.endTime)}</p>`;
    }
  } else if (element.value.type === "bpmn:EndEvent" && processInstance.value) {
    html = "";
    if (processInstance.value.result) {
      dataResult = getDictLabel(
        dictData[DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT].value,
        processInstance.value.result
      );
      html += `<p>结果：${dataResult}</p>`;
    }
    if (activity.endTime) {
      html += `<p>结束时间：${formatDate(activity.endTime)}</p>`;
    }
  }
  return `<div class="element-overlays">${html}</div>`
}

/**
 * 流程图的元素被 hover时，展示悬浮提示
 * @param element
 */
const elementHover = (element) => {
  const activityList = activityLists.value;
  if (!activityList || activityList.length === 0) {
    return;
  }
  element.value = element;
  !elementOverlayIds.value && (elementOverlayIds.value = {});
  !overlays.value && (overlays.value = bpmnModeler.get("overlays"));
  // 展示信息
  // console.log(activityLists.value, 'activityLists.value')
  // console.log(element.value, 'element.value')
  // debugger
  //活动是时间升序，所以进行倒序查找。以便找到相同活动key用最新的数据
  const activity = activityList.findLast((m) => m.key === element.value.id);
  // console.log(activity, 'activityactivityactivityactivity')
  if (!activity) {// || activity.type == "exclusiveGateway" || activity.type == "parallelGateway"
    return;
  }
  if (!elementOverlayIds.value[element.value.id] && element.value.type !== "bpmn:Process") {
    let html = getHoverHtml(element, processInstance, activity)
    // console.log(html, 'html111111111111111')
    elementOverlayIds.value[element.value.id] = toRaw(overlays.value)?.add(
      element.value,
      {
        position: { left: 0, bottom: 0 },
        html: html,
      }
    );
  }
};

// 流程图的元素被 out
const elementOut = (element) => {
  let overlaysRaw = toRaw(overlays.value);
  if (overlaysRaw) {
    overlaysRaw.remove({ element });
  }
  if (elementOverlayIds.value) {
    elementOverlayIds.value[element.id] = null;
  }
};

function init(res) {
  let actvieRes = res ? res : props;
  xml.value = actvieRes.value;
  activityLists.value = actvieRes.activityData;
  processInstance.value = actvieRes.processInstanceData;
  taskList.value = actvieRes.taskData;
  // 初始化
  initBpmnModeler();
  createNewDiagram(actvieRes.value);
  // 初始模型的监听器
  initModelListeners();
}

onMounted(() => {
  //虽然watch+immediate时此处可以不设置，但是initBpmnModeler没执行会导致空指针，所以这里需要设置。
  if (props.processInstanceId && props.processInstanceId != "") {
    // 调用接口获取流程数据
    getProcessViewInfo(props.processInstanceId).then((data) => {
      let { processInstanceData, activityData, taskData, bpmnXML } = data;
      init({ value: bpmnXML, taskData, activityData, processInstanceData });
    });
  } else if (props.processDefinitionKey && props.processDefinitionKey != "") {
    // 调用接口获取流程xml数据
    getBpmnXmlByProcessDefinitionKey(props.processDefinitionKey).then((res) => {
      if (res.data) {
        let bpmnXML = res.data;
        init({ value: bpmnXML });
      } else {
        console.error("未获取到processDefinitionKey对应的流程图xml数据");
      }
    });
  } else if (props.value && props.value != "") {
    init();
  } else {
    let msg =
      "请设置processInstanceId、processDefinitionKey或者value，否则无法预览流程图";
    proxy.$modal.msgError(msg);
    console.error(msg);
  }
});
onBeforeUnmount(() => {
  if (bpmnModeler) {
    bpmnModeler.destroy();
  }
  emit("destroy", bpmnModeler);
  bpmnModeler = null;
});

watch(
  () => props.processInstanceId,
  () => {
    getProcessViewInfo(props.processInstanceId).then((data) => {
      let { processInstanceData, activityData, taskData, bpmnXML } = data;
      init({ value: bpmnXML, taskData, activityData, processInstanceData });
    });
  }
);

watch(
  () => props.value,
  (newValue) => {
    xml.value = newValue;
    createNewDiagram(xml.value);
  }
);
watch(
  () => props.activityData,
  (newActivityData) => {
    activityLists.value = newActivityData;
    createNewDiagram(xml.value);
  }
);
watch(
  () => props.processInstanceData,
  (newProcessInstanceData) => {
    processInstance.value = newProcessInstanceData;
    createNewDiagram(xml.value);
  }
);
watch(
  () => props.taskData,
  (newTaskListData, oldTaskListData) => {
    taskList.value = newTaskListData;
    createNewDiagram(xml.value);
  },
  { deep: false, immediate: false }
);

// 图例
let legendList = ref([
  { name: "绿色", color: "green", text: "通过" },
  { name: "橙色", color: "#e6a23c", text: "退回" },
  { name: "蓝色", color: "#1890ff", text: "审批中" },
  { name: "红色", color: "red", text: "不通过" },
  // {name:'绿色',color:"green",text:'待审批'},
]);
</script>

<style lang="scss">
.bpm-window-el-dialog > .el-dialog__body {
  height: 86vh;
  overflow: auto;
  padding: 0;
}
/* 格子背景 */
.my-process-designer
  .my-process-designer__container
  .my-process-designer__canvas {
  background: url()
    repeat !important;
}
.my-process-designer__container {
  position: relative;
  .my-legend {
    position: absolute;
    bottom: 0;
    left: 10px;
    color: #000;
    > p {
      > span:first-child {
        display: inline-block;
        width: 12px;
        height: 12px;
        border-radius: 50%;
        margin-right: 6px;
      }
    }
  }
}

/** 处理中 */
.highlight-todo.djs-connection > .djs-visual > path {
  stroke: #1890ff !important;
  stroke-dasharray: 4px !important;
  fill-opacity: 0.2 !important;
}

.highlight-todo.djs-shape .djs-visual > :nth-child(1) {
  fill: #1890ff !important;
  stroke: #1890ff !important;
  stroke-dasharray: 4px !important;
  fill-opacity: 0.2 !important;
}

:deep(.highlight-todo.djs-connection > .djs-visual > path) {
  stroke: #1890ff !important;
  stroke-dasharray: 4px !important;
  fill-opacity: 0.2 !important;
  marker-end: url("#sequenceflow-end-_E7DFDF-_E7DFDF-803g1kf6zwzmcig1y2ulm5egr");
}

:deep(.highlight-todo.djs-shape .djs-visual > :nth-child(1)) {
  fill: #1890ff !important;
  stroke: #1890ff !important;
  stroke-dasharray: 4px !important;
  fill-opacity: 0.2 !important;
}

/** 通过 */
.highlight.djs-shape .djs-visual > :nth-child(1) {
  fill: green !important;
  stroke: green !important;
  fill-opacity: 0.2 !important;
}

.highlight.djs-shape .djs-visual > :nth-child(2) {
  fill: green !important;
}

.highlight.djs-shape .djs-visual > path {
  fill: green !important;
  fill-opacity: 0.2 !important;
  stroke: green !important;
}

.highlight.djs-connection > .djs-visual > path {
  stroke: green !important;
}

.highlight:not(.djs-connection) .djs-visual > :nth-child(1) {
  fill: green !important; /* color elements as green */
}

:deep(.highlight.djs-shape .djs-visual > :nth-child(1)) {
  fill: green !important;
  stroke: green !important;
  fill-opacity: 0.2 !important;
}

:deep(.highlight.djs-shape .djs-visual > :nth-child(2)) {
  fill: green !important;
}

:deep(.highlight.djs-shape .djs-visual > path) {
  fill: green !important;
  fill-opacity: 0.2 !important;
  stroke: green !important;
}

:deep(.highlight.djs-connection > .djs-visual > path) {
  stroke: green !important;
}

/** 不通过 */
.highlight-reject.djs-shape .djs-visual > :nth-child(1) {
  fill: red !important;
  stroke: red !important;
  fill-opacity: 0.2 !important;
}

.highlight-reject.djs-shape .djs-visual > :nth-child(2) {
  fill: red !important;
}

.highlight-reject.djs-shape .djs-visual > path {
  fill: red !important;
  fill-opacity: 0.2 !important;
  stroke: red !important;
}

.highlight-reject.djs-connection > .djs-visual > path {
  stroke: red !important;
}

.highlight-reject:not(.djs-connection) .djs-visual > :nth-child(1) {
  fill: red !important; /* color elements as green */
}

:deep(.highlight-reject.djs-shape .djs-visual > :nth-child(1)) {
  fill: red !important;
  stroke: red !important;
  fill-opacity: 0.2 !important;
}

:deep(.highlight-reject.djs-shape .djs-visual > :nth-child(2)) {
  fill: red !important;
}

:deep(.highlight-reject.djs-shape .djs-visual > path) {
  fill: red !important;
  fill-opacity: 0.2 !important;
  stroke: red !important;
}

:deep(.highlight-reject.djs-connection > .djs-visual > path) {
  stroke: red !important;
}

/** 已取消 */
.highlight-cancel.djs-shape .djs-visual > :nth-child(1) {
  fill: grey !important;
  stroke: grey !important;
  fill-opacity: 0.2 !important;
}

.highlight-cancel.djs-shape .djs-visual > :nth-child(2) {
  fill: grey !important;
}

.highlight-cancel.djs-shape .djs-visual > path {
  fill: grey !important;
  fill-opacity: 0.2 !important;
  stroke: grey !important;
}

.highlight-cancel.djs-connection > .djs-visual > path {
  stroke: grey !important;
}

.highlight-cancel:not(.djs-connection) .djs-visual > :nth-child(1) {
  fill: grey !important; /* color elements as green */
}

:deep(.highlight-cancel.djs-shape .djs-visual > :nth-child(1)) {
  fill: grey !important;
  stroke: grey !important;
  fill-opacity: 0.2 !important;
}

:deep(.highlight-cancel.djs-shape .djs-visual > :nth-child(2)) {
  fill: grey !important;
}

:deep(.highlight-cancel.djs-shape .djs-visual > path) {
  fill: grey !important;
  fill-opacity: 0.2 !important;
  stroke: grey !important;
}

:deep(.highlight-cancel.djs-connection > .djs-visual > path) {
  stroke: grey !important;
}

/** 回退 */
.highlight-return.djs-shape .djs-visual > :nth-child(1) {
  fill: #e6a23c !important;
  stroke: #e6a23c !important;
  fill-opacity: 0.2 !important;
}

.highlight-return.djs-shape .djs-visual > :nth-child(2) {
  fill: #e6a23c !important;
}

.highlight-return.djs-shape .djs-visual > path {
  fill: #e6a23c !important;
  fill-opacity: 0.2 !important;
  stroke: #e6a23c !important;
}

.highlight-return.djs-connection > .djs-visual > path {
  stroke: #e6a23c !important;
}

.highlight-return:not(.djs-connection) .djs-visual > :nth-child(1) {
  fill: #e6a23c !important; /* color elements as green */
}

:deep(.highlight-return.djs-shape .djs-visual > :nth-child(1)) {
  fill: #e6a23c !important;
  stroke: #e6a23c !important;
  fill-opacity: 0.2 !important;
}

:deep(.highlight-return.djs-shape .djs-visual > :nth-child(2)) {
  fill: #e6a23c !important;
}

:deep(.highlight-return.djs-shape .djs-visual > path) {
  fill: #e6a23c !important;
  fill-opacity: 0.2 !important;
  stroke: #e6a23c !important;
}

:deep(.highlight-return.djs-connection > .djs-visual > path) {
  stroke: #e6a23c !important;
}

.element-overlays {
  width: 360px;
  padding: 8px;
  color: #fafafa;
  background: rgb(0 0 0 / 60%);
  border-radius: 4px;
  box-sizing: border-box;
}

.bjs-powered-by {
  display: none;
}
</style>
